package com.jugheadzhou.auth.service.impl;

import cn.hutool.crypto.SecureUtil;
import com.jugheadzhou.auth.core.auth.LoginUser;
import com.jugheadzhou.auth.core.auth.SystemContext;
import com.jugheadzhou.auth.core.common.exception.BusinessException;
import com.jugheadzhou.auth.core.message.WsEvent;
import com.jugheadzhou.auth.core.message.WsMessage;
import com.jugheadzhou.auth.core.message.producer.meta.AuthRabbitMeta;
import com.jugheadzhou.auth.core.message.producer.sender.RabbitSender;
import com.jugheadzhou.auth.core.websocket.WebSocketManager;
import com.jugheadzhou.auth.dto.LoginDto;
import com.jugheadzhou.auth.entity.User;
import com.jugheadzhou.auth.entity.UserScore;
import com.jugheadzhou.auth.entity.UserToken;
import com.jugheadzhou.auth.mapper.UserMapper;
import com.jugheadzhou.auth.mapper.UserScoreMapper;
import com.jugheadzhou.auth.mapper.UserTokenMapper;
import com.jugheadzhou.auth.service.UserService;
import com.jugheadzhou.auth.util.HttpContextUtil;
import com.jugheadzhou.auth.util.JWTUtils;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author jugheadzhou
 * @date 2021-11-27
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

    private static final String CLAIM_KEY_USERNAME = "sub";
    private static final String CLAIM_KEY_USER = "user";
    private static final String CLAIM_KEY_USERID = "uerid";
    private static final String CLAIM_KEY_NAME = "name";
    private static final String CLAIM_KEY_ROLES = "roles";
    private static final String CLAIM_KEY_DEPTNAME = "detptname";
    private static final String CLAIM_KEY_CREATED = "created";

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserTokenMapper userTokenMapper;

    @Resource
    private UserScoreMapper userScoreMapper;

    @Resource
    private WebSocketManager webSocketManager;

    @Resource
    private RabbitSender rabbitSender;

    @Override
    public LoginUser login(LoginDto loginDto) {
        HttpServletRequest servletRequest = HttpContextUtil.getHttpServletRequest();
        UserAgent userAgent = UserAgent.parseUserAgentString(servletRequest.getHeader("User-Agent"));
        String browserName = userAgent.getBrowser().getName();

        String password = SecureUtil.md5(loginDto.getPassword() + loginDto.getUsername());
        User user = userMapper.createLambdaQuery().andEq(User::getUsername, loginDto.getUsername()).singleSimple();
        if (user == null) {
            throw new BusinessException("账号错误");
        }
        if (!password.equals(user.getPassword())) {
            throw new BusinessException("密码错误");
        }
        String token = createToken(user);
        log.info("登陆生成token: {}", token);
        LoginUser loginUser = new LoginUser();
        BeanUtils.copyProperties(user, loginUser);
        loginUser.setToken(token);

        // 新增登陆记录
        UserToken userToken = new UserToken();
        userToken.setUserId(user.getId());
        userToken.setUsername(user.getUsername());
        userToken.setNickname(user.getNickname());
        userToken.setLoginTime(LocalDateTime.now());
        userToken.setToken(token);
        // 取jwt过期时间的0.8倍, 保证jwt token的可用性
        userToken.setExpireTime(LocalDateTime.now().plusSeconds((long) (JWTUtils.EXPIRATION_TIME * 0.8 / 1000)));
        userToken.setDeleted(0);
        userTokenMapper.insertTemplate(userToken);
        return loginUser;
    }

    @Override
    public void logout() {
        LoginUser loginUser = SystemContext.getUser();
        String token = JWTUtils.TOKEN_PREFIX + SystemContext.getToken();
        UserToken userToken = userTokenMapper.createLambdaQuery()
                .andEq(UserToken::getUsername, loginUser.getUsername())
                .andEq(UserToken::getToken, token)
                .andEq(UserToken::getDeleted, 0)
                .singleSimple();
        if (userToken != null) {
            userToken.setDeleted(1);
            userTokenMapper.updateTemplateById(userToken);
            log.info("{}已退出登陆", loginUser.getNickname());
        }
    }

    @Override
    public User info() {
        LoginUser loginUser = SystemContext.getUser();
        User user = userMapper.single(loginUser.getId());
        user.setPassword(null);
        return user;
    }

    @Override
    public void updateScore(String username, BigDecimal score) {
        UserScore userScore = userScoreMapper.createLambdaQuery().andEq(UserScore::getUsername, username).singleSimple();
        userScore.setScore(score);
        userScoreMapper.updateTemplateById(userScore);
        // TODO connectionId
        List<UserToken> loginRecords = userTokenMapper.createLambdaQuery()
                .andEq(UserToken::getDeleted, 0)
                .andEq(UserToken::getUsername, username)
                .andGreat(UserToken::getExpireTime, LocalDateTime.now()).select();
        if (!CollectionUtils.isEmpty(loginRecords)) {
            loginRecords.forEach(record -> {
//                webSocketManager.sendMessageAsync(record.getToken(), score.toString());
                WsMessage wsMessage = WsMessage.builder().connectionId(record.getToken()).wsEvent(WsEvent.ON_MESSAGE.getName()).content(score.toString()).build();
                rabbitSender.send(AuthRabbitMeta.WS_GLOBAL, wsMessage);
            });
        }

    }

    @Override
    public String selectScoreByWsConnectionId(String connectionId) {
        BigDecimal userScore = userScoreMapper.selectScoreByWsConnectionId(connectionId);
        return userScore == null ? "0" : userScore.toString();
    }

    /**
     * 通过websocket连接ID查询初始分数并发送给消息队列
     *
     * @param connectionId
     * @return
     */
    @Override
    public void selectInitScoreAndSendMqByWsConnectionId(String connectionId) {
        WsMessage wsMessage = WsMessage
                .builder()
                .connectionId(connectionId)
                .wsEvent(WsEvent.ON_MESSAGE.getName())
                .content(selectScoreByWsConnectionId(connectionId))
                .build();
        rabbitSender.send(AuthRabbitMeta.WS_GLOBAL, wsMessage);
    }

    public static String createToken(User user) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, user.getUsername());
        claims.put(CLAIM_KEY_USER, user.getDeptId());
        claims.put(CLAIM_KEY_USERID, user.getId());
        claims.put(CLAIM_KEY_DEPTNAME, user.getDeptName());
        claims.put(CLAIM_KEY_NAME, user.getNickname());
        claims.put(CLAIM_KEY_ROLES, user.getRoles());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return JWTUtils.createToken(claims);
    }
}
